<?php

namespace App\Traits;

use App\Models\Api\Chat;
use App\Models\Api\User;
use Illuminate\Support\Facades\Cache;

trait ChatBase
{
    public $SOCKET_USER = 'socket:user:';
    public $ACCESS_TOKEN = 'access_token:';

    public function start($token, $type, $ws, $fd)
    {
        $user = $this->getUser($token);
        if (empty($user)) {
            $ws->close($fd);
        } else {
            $ws->bind($fd, $user['id']);
            if (strpos($type, 'chat_uid_') !== false) {
                $this->setFd($ws, $user['id'], $fd, $type);
            }
            if ($type == 'index') {
                $this->setFd($ws, $user['id'], $fd, $type);
                $this->readDelay($ws, $user['id'], $fd);
            }

            $this->success($ws, $fd, $this->getSocket($user['id']));
        }
    }

    /**
     * 获取用户 socket 信息
     * @param $uid
     * @return mixed
     */
    public function getSocket($uid)
    {
        return Cache::get($this->SOCKET_USER . $uid);
    }

    /**
     * 设置用户 socket 信息
     * @param $uid
     * @param $data
     * @return void
     */
    public function setSocket($uid, $data)
    {
        Cache::put($this->SOCKET_USER . $uid, $data);

    }

    /**
     * 删除用户 socket 信息
     * @param $uid
     * @return bool
     */
    public function delSocket($uid)
    {
        //return Cache::forget($this->SOCKET_USER . $uid);
        $socket = $this->getSocket($uid);
        unset($socket['fd']['index']);
        $this->setSocket($uid, $socket);
        return true;
    }

    /**
     * 获取 access_token 缓存 用户信息
     * @param $token
     * @return mixed
     */
    public function getUser($token)
    {
        return Cache::get($this->ACCESS_TOKEN . $token);
    }

    /**
     * 设置 access_token 缓存 用户信息
     * @param $token
     * @param $userInfo
     * @return void
     */
    public function setUser($token, $userInfo)
    {
        Cache::put($this->ACCESS_TOKEN . $token, [
            'id' => $userInfo->id,
            'user_name' => $userInfo->user_name,

        ]);
    }

    /**
     * 删除 access_token 缓存
     * @param $token
     * @return bool
     */
    public function delUser($token): bool
    {
        return Cache::forget($this->ACCESS_TOKEN . $token);
    }

    /**
     * 获取 ws 绑定客户端 uid
     * @param $ws
     * @param $fd
     * @return int|mixed
     */
    public function getBindUid($ws, $fd)
    {
        $info = $ws->getClientInfo($fd);
        return empty($info['uid']) ? 0 : $info['uid'];
    }

    /**
     * 设置 fd 消息
     * @param $ws
     * @param $uid
     * @param $fd
     * @param $type
     * @return void
     */
    public function setFd($ws, $uid, $fd, $type)
    {
        $data = $this->getSocket($uid);
        $data['fd'][$type] = $fd;
        foreach ($data['fd'] as $key => $value) {
            $bindUid = $this->getBindUid($ws, $value);     // Get the connection info by file description
            if (empty($bindUid) || $bindUid !== $uid) {
                unset($data['fd'][$key]);
            }
        }
        $this->setSocket($uid, $data);
    }

    /**
     * ws 发送延迟消息
     * @param $ws
     * @param $uid
     * @param $fd
     * @return void
     */
    public function readDelay($ws, $uid, $fd)
    {
        $data = $this->getSocket($uid);
        // 发送添加好友请求
        if (!empty($data['apply_list'])) {
            foreach ($data['apply_list'] as $uid => $value) {
                $user = (new User)->getUserById($uid);
                if (empty($user->id)) {
                    unset($data['apply_list'][$uid]);
                    continue;
                }
                $this->success($ws, $fd, [
                    'type' => 'addFriend',
                    'from' => $uid,
                    'user_name' => $user->user_name,
                    'message' => $value
                ]);
            }
        }
        // 发送延迟按消息
        if (!empty($data['delay_list'])) {
            foreach ($data['delay_list'] as $key => $value) {
                $this->success($ws, $fd, [
                    'type' => 'chat',
                    'uid' => $key,
                    'count' => $value['count'],
                    'message' => $value['message']
                ]);
                unset($data['delay_list'][$key]);
            }
        }
        $this->setSocket($uid, $data);
    }

    /**
     * 消息类型
     * @param $ws
     * @param $frame
     * @return void
     */
    public function switchBoard($ws, $frame)
    {
        $data = json_decode($frame->data, true);
        switch ($data['type']) {
            case 'addFriend':
                $this->addFriend($ws, $frame->fd, $data);
                break;
            case 'chat':
                $this->chat($ws, $frame->fd, $data);
                break;
            case 'handleFriend':
                $this->handleFriend($ws, $frame->fd, $data);
                break;
        }
    }

    /**
     * ws 发送添加好友请求
     * @param $ws
     * @param $fd
     * @param $data
     * @return void
     */
    private function addFriend($ws, $fd, $data)
    {
        $socket = $this->getSocket($data['target']);
        $socket['apply_list'][$data['uid']] = $data['target'];
        if (!empty($socket['fd']['index'])) { // 用户在线
            $this->success($ws, $socket['fd']['index'], [
                'type' => 'addFriend',
                'from' => $data['uid'],
                'user_name' => $data['user_name'],
                'message' => $data['message'],
            ]);
        }
        $this->setSocket($data['target'], $socket);
        $this->success($ws, $fd, null);
    }

    /**
     *
     * @param $ws
     * @param $fd
     * @param $data
     * @return void
     */
    private function chat($ws, $fd, $data)
    {
        $uid = $this->getBindUid($ws, $fd);
        $fid = $data['uid'];

        (new Chat())->insert($uid, $fid, $data['message']); // 保存聊天记录
        $socket = $this->getSocket($fid); // 对方 redis 记录
        if (!empty($socket['fd']['chat_uid_' . $uid])) { // 对话窗口存在, fd 对话窗口id
            $this->success($ws, $socket['fd']['chat_uid_' . $uid], [
                'message' => $data['message']
            ]);
        } else if (!empty($socket['fd']['index'])) { // 客户端在线
            $this->success($ws, $socket['fd']['index'], [
                'type' => 'chat',
                'uid' => $uid,
                'count' => $data['count'] ?: 1,
                'message' => $data['message'],
            ]);
        } else { // 离线
            if (!empty($socket['delay_list'][$uid])) {  // 延时队列存在
                $socket['delay_list'][$uid]['count'] += 1;
                $socket['delay_list'][$uid]['message'] = $data['message'];
            } else {
                $socket['delay_list'][$uid] = [
                    'count' => 1,
                    'message' => $data['message'],
                ];
            }
            $this->setSocket($fid, $socket);
        }
    }

    /**
     * 发送好友处理结果
     * @param $ws
     * @param $fd
     * @param $data
     * @return void
     */
    private function handleFriend($ws, $fd, $data)
    {
        $socket = $this->getSocket($data['uid']);
        if (!empty($socket['fd']['index'])) { // 用户在线
            $this->success($ws, $socket['fd']['index'], [
                'type' => 'handleFriend',
                'from' => $data['target'],
                'user_name' => $data['user_name'],
                'message' => $data['message'],
                'desc' => $data['desc'],
            ]);
        }
    }

    public function success($ws, $fd, $data)
    {
        $this->show($ws, $fd, 0, '操作成功', $data);
    }

    public function fail($ws, $fd)
    {
        $this->show($ws, $fd, 1, '操作失败', null);
    }

    /**
     * 发送 websocket 消息
     * @param $ws
     * @param $fd
     * @param $status
     * @param $message
     * @param $result
     * @return void
     */
    public function show($ws, $fd, $status, $message, $result)
    {
        $data = [
            'code' => $status,
            'message' => $message,
            'data' => $result,
        ];
        $ws->push($fd, json_encode($data));
    }
}
